#include "StrDiff.h"
#include "TestKnowledgeBaseWriter.h"
#include "Tests.h"
#include <iostream>

namespace TestKnowledgeBaseWriter
{

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void RunAllTests ()
{
	TestTextWriter::Run ();
	TestFileWriter::Run ();
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

TestBase::TestBase ()
{
	memory = KnowledgeBaseMemoryFake::Spawn ();
	textStream = TextStreamFake::Spawn ();
}

TestBase::~TestBase ()
{
	while ( ! memory->elements.empty () )
	{
		memory->elements.begin ()->Delete ();
		memory->elements.erase ( memory->elements.begin () );
	}
	textStream.Delete ();
	memory.Delete ();
}

std::string TestBase::SuiteIdentity ()
{
	return "TestKnowledgeBaseWriter";
}

void TestBase::Check ()
{
	std::string actualOutput = textStream->written;
	if ( actualOutput != expectedOutput )
	{
		std::cout << StrDiff ( actualOutput, expectedOutput );
		std::cout << " != " << "\n\r";
		std::cout << StrDiff ( expectedOutput, actualOutput );
		std::cout << "\n\r";	
	}
	TEST_ASSUMPTION ( actualOutput == expectedOutput );
}

void TestBase::AddElement 
	( int seniorKey
	, int seniorType
	, std::string lexemText
	, int upperBranch
	, int lowerBranch
	, int nextLevel
	, int synonym
	, int adjacentAscending
	, int adjacentDescending
	, int beforeAdjacentAscending
	, int beforeAdjacentDescending
	, int greaterAscending
	, int greaterDescending
	, int lesserAscending
	, int lesserDescending
	)
{
	KnowledgeBaseElementInterfacePtr element ( KnowledgeBaseElement::Spawn ( lexemText ) );
	element->SetSeniorKey ( seniorKey );
	element->SetSeniorType ( seniorType );
	element->SetUpperBranch ( upperBranch );
	element->SetLowerBranch ( lowerBranch );
	element->SetNextLevel ( nextLevel );
	element->SetSynonym ( synonym );
	element->SetAdjacentAscending ( adjacentAscending );
	element->SetAdjacentDescending ( adjacentDescending );
	element->SetBeforeAdjacentAscending ( beforeAdjacentAscending );
	element->SetBeforeAdjacentDescending ( beforeAdjacentDescending );
	element->SetGreaterAscending ( greaterAscending );
	element->SetGreaterDescending ( greaterDescending );
	element->SetLesserAscending ( lesserAscending );
	element->SetLesserDescending ( lesserDescending );
	memory->elements.push_back ( element );
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

TestTextWriter::TestTextWriter ()
{
	writerLocalization = KnowledgeBaseWriterTextLocalizationFake::Spawn ();
	writerLocalization->globalAddress = "Global\naddress\n\n\n\n\n\n\n\n";
	writerLocalization->seniorKey = "Senior\nkey";
	writerLocalization->seniorType = "\n\nSenior\ntype";
	writerLocalization->lexemText = "Lexem\n\n\nT\nE\n\n\nX\nT";
	writerLocalization->upperBranch = "Upper\nbranch";
	writerLocalization->lowerBranch = "Lower\nbranch";
	writerLocalization->nextLevel = "Next level";
	writerLocalization->synonym = "Synonym";
	writerLocalization->adjacentAscending = "Adjacent\nascending";
	writerLocalization->adjacentDescending = "Adjacent\ndescending";
	writerLocalization->beforeAdjacentAscending = "Before\nadjacent\nascending";
	writerLocalization->beforeAdjacentDescending = "Before\nadjacent\ndescending";
	writerLocalization->greaterAscending = "Greater\nascending";
	writerLocalization->greaterDescending = "Greater\ndescending";
	writerLocalization->lesserAscending = "Lesser\nascending";
	writerLocalization->lesserDescending = "Lesser\ndescending";
	writerLocalization->typeRoot = "Type root";
	writerLocalization->valueRoot = "Value root";
	memory->typeRootAddress = 1;
	memory->valueRootAddress = 2;
	
	AddElement ( 0, 0, "null elem with extraordinary long name", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 );
	AddElement ( 0x10, 0x00000123,  "first elem",  true, false, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0 );
	AddElement ( 0x11, 0x12345678, "second elem", false,  true, 0x41, 0x51, 0x61, 0x71, 0x81, 0x91, 0xA1, 0xB1, 0xC1, 0xD1 );
	AddElement ( 0x12, 0x00123456,  "third elem",  true, false, 0x42, 0x52, 0x62, 0x72, 0x82, 0x92, 0xA2, 0xB2, 0xC2, 0xD2 );

	expectedOutput =
		"+----------+----------+----------+------------+--------+--------+------------+----------+-----------+------------+-----------+------------+-----------+------------+-----------+------------+\n\r"
		"|   Global |   Senior |   Senior |      Lexem |  Upper |  Lower | Next level |  Synonym |  Adjacent |   Adjacent |    Before |     Before |   Greater |    Greater |    Lesser |     Lesser |\n\r"
		"|  address |      key |     type |          T | branch | branch |            |          | ascending | descending |  adjacent |   adjacent | ascending | descending | ascending | descending |\n\r"
		"|          |          |          |          E |        |        |            |          |           |            | ascending | descending |           |            |           |            |\n\r"
		"|          |          |          |          X |        |        |            |          |           |            |           |            |           |            |           |            |\n\r"
		"|          |          |          |          T |        |        |            |          |           |            |           |            |           |            |           |            |\n\r"
		"+----------+----------+----------+------------+--------+--------+------------+----------+-----------+------------+-----------+------------+-----------+------------+-----------+------------+\n\r"
		"| 00000001 | 00000010 | 00000123 |  Type root |      1 |      0 |   00000040 | 00000050 |  00000060 |   00000070 |  00000080 |   00000090 |  000000A0 |   000000B0 |  000000C0 |   000000D0 |\n\r"
		"| 00000002 | 00000011 | 12345678 | Value root |      0 |      1 |   00000041 | 00000051 |  00000061 |   00000071 |  00000081 |   00000091 |  000000A1 |   000000B1 |  000000C1 |   000000D1 |\n\r"
		"| 00000003 | 00000012 | 00123456 | third elem |      1 |      0 |   00000042 | 00000052 |  00000062 |   00000072 |  00000082 |   00000092 |  000000A2 |   000000B2 |  000000C2 |   000000D2 |\n\r"
		"+----------+----------+----------+------------+--------+--------+------------+----------+-----------+------------+-----------+------------+-----------+------------+-----------+------------+\n\r"
	;
}

TestTextWriter::~TestTextWriter ()
{
	writerLocalization.Delete ();
}

std::string TestTextWriter::TestIdentity ()
{
	return "TestTextWriter";
}

void TestTextWriter::RunSingle ()
{
	KnowledgeBaseWriterInterfacePtr writer = KnowledgeBaseWriterText::Spawn ( textStream, writerLocalization );
	writer->Write ( memory );
	writer.Delete ();
	Check ();
}

void TestTextWriter::Run ()
{
	TestTextWriter ().RunSingle ();
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

TestFileWriter::TestFileWriter ()
{
	memory->typeRootAddress = 1;
	memory->valueRootAddress = 2;
	const std::string veryLongText = "one two three four five six seven eight nine ten eleven twelve thirteen fourteen";
	AddElement (    0,          0,  veryLongText,     0,     0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0 );
	AddElement ( 0x10, 0x12345678,  "first elem",  true, false, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0 );
	AddElement ( 0x11, 0x9abcdef0, "second elem", false,  true, 0x41, 0x51, 0x61, 0x71, 0x81, 0x91, 0xA1, 0xB1, 0xC1, 0xD1 );
	AddElement ( 0x12, 0xfedcba98,  "third elem",  true, false, 0x42, 0x52, 0x62, 0x72, 0x82, 0x92, 0xA2, 0xB2, 0xC2, 0xD2 );
	AddElement ( 0x13, 0xffffffff,  veryLongText, false,  true, 0x43, 0x53, 0x63, 0x73, 0x83, 0x93, 0xA3, 0xB3, 0xC3, 0xD3 );
	expectedOutput =
		"000000010000001012345678                                                              10000000400000005000000060000000700000008000000090000000A0000000B0000000C0000000D0\n\r"
		"00000002000000119ABCDEF0                                                              01000000410000005100000061000000710000008100000091000000A1000000B1000000C1000000D1\n\r"
		"0000000300000012FEDCBA98third elem                                                    10000000420000005200000062000000720000008200000092000000A2000000B2000000C2000000D2\n\r"
		"0000000400000013FFFFFFFFone two three four five six seven eight nine ten eleven twelve01000000430000005300000063000000730000008300000093000000A3000000B3000000C3000000D3\n\r"
	;
}

std::string TestFileWriter::TestIdentity ()
{
	return "TestFileWriter";
}

void TestFileWriter::RunSingle ()
{
	KnowledgeBaseWriterInterfacePtr writer = KnowledgeBaseWriterFile::Spawn ( textStream );
	writer->Write ( memory );
	writer.Delete ();
	Check ();
}

void TestFileWriter::Run ()
{
	TestFileWriter ().RunSingle ();
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

}; // namespace TestKnowledgeBaseWriter
